#ifndef CONIN_UI_CONTROL_H
#define CONIN_UI_CONTROL_H

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include "win32titlebarctl.h"

namespace cnc {

	namespace ui {
	
		class Control_
		{
		protected:
			HWND hwndControl_; // handle to control provided by CreateWindow
		public:
			Control_(HWND hwndControl = NULL) : hwndControl_(hwndControl)
			{
			}
		};
	
		class ColoredText : public virtual Control_
		{
		protected:
			void setupTextColor()
			{
				HGLOBAL hmem = GlobalAlloc(GHND, sizeof(COLORREF));
				COLORREF *lpcr = static_cast<COLORREF *>(GlobalLock(hmem));
				*lpcr = RGB(0, 0, 0);
				GlobalUnlock(hmem);
				SetProp(hwndControl_, TEXT("textColor"), hmem);
			}
			void removeTextColor()
			{
				HGLOBAL hmem = RemoveProp(hwndControl_, TEXT("textColor"));
				GlobalFree(hmem);
			}
		public:
			void setTextColor(COLORREF cr)
			{
				HGLOBAL hmem = GetProp(hwndControl_, TEXT("textColor"));
				COLORREF *lpcr = static_cast<COLORREF *>(GlobalLock(hmem));
				*lpcr = cr;
				GlobalUnlock(hmem);
			}
			COLORREF getTextColor() const
			{
				COLORREF cr;
				HGLOBAL hmem = GetProp(hwndControl_, TEXT("textColor"));
				COLORREF *lpcr = static_cast<COLORREF *>(GlobalLock(hmem));
				cr = *lpcr;
				GlobalUnlock(hmem);
				return cr;
			}
		};
	
		class RefCounted : public virtual Control_
		{
		protected:
			void setRefCount(LONG refCount)
			{
				SetWindowLong(hwndControl_, GWL_USERDATA, refCount);
			}
			LONG getRefCount() const
			{
				return GetWindowLong(hwndControl_, GWL_USERDATA);
			}
		public:
			void addRef()
			{
				LONG refCount = GetWindowLong(hwndControl_, GWL_USERDATA) + 1;
				SetWindowLong(hwndControl_, GWL_USERDATA, refCount);
			}
			void release()
			{
				LONG refCount = GetWindowLong(hwndControl_, GWL_USERDATA) - 1;
				SetWindowLong(hwndControl_, GWL_USERDATA, refCount);
			}
		};
	
		class Control : public virtual Control_,
			public ColoredText, public RefCounted
		{
		public:
			Control(HWND hwndParent,
				LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle,
				int x, int y, int nWidth, int nHeight,
				int nIDDlgItem, LPVOID lpParam = NULL)
				: Control_(NULL)
			{
				HINSTANCE hInstance = GetWindowInstance(hwndParent);
				HMENU hMenu = reinterpret_cast<HMENU>(nIDDlgItem);
				hwndControl_ = CreateWindowEx(0, lpClassName, lpWindowName,
					dwStyle | WS_CHILD | WS_VISIBLE,
					x, y, nWidth, nHeight, hwndParent, hMenu, hInstance, lpParam);
				RefCounted::setRefCount(1);
				ColoredText::setupTextColor();
			}
			Control(HWND hwndControl) : Control_(hwndControl)
			{
				hwndControl_ = hwndControl;
				LONG refCount = RefCounted::getRefCount() + 1;
				RefCounted::setRefCount(refCount);
			}
			virtual ~Control()
			{
				LONG refCount = RefCounted::getRefCount(/*hwndControl_*/);
				if (refCount == 1) {
					ColoredText::removeTextColor();
					DestroyWindow(hwndControl_);
				} else
					RefCounted::setRefCount(/*hwndControl_, */refCount - 1);
			}
			int getID() const
			{
				return static_cast<int>(::GetWindowLong(hwndControl_, GWL_ID));
			}
			HWND getHandle() const
			{
				return hwndControl_;
			}
			void setFont(HFONT hFont, bool redraw = true)
			{
				SetWindowFont(hwndControl_, hFont, redraw ? TRUE : FALSE);
			}
			int getTextLength() const
			{
				return GetWindowTextLength(hwndControl_);
			}
			LPCTSTR getText() const
			{
				static TCHAR lpString[256];
				GetWindowText(hwndControl_, lpString, 256);
				return lpString;
			}
			void setText(LPCTSTR szText)
			{
				SetWindowText(hwndControl_, szText);
			}
			virtual void color(HDC hdc, int /*type*/) // to be overriden in subclasses
			{
				SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
			}
		};
	
		class Static : public Control
		{
		public:
			Static(HWND hwndParent,
				LPCTSTR lpWindowName, int x, int y,
				int nWidth, int nHeight, int nIDDlgItem, DWORD dwStyle = 0)
				: Control(hwndParent,
					TEXT("static"), lpWindowName, dwStyle, x, y,
					nWidth, nHeight, nIDDlgItem) {}
			Static(HWND hwndControl) : Control(hwndControl) {}
			void color(HDC hdc, int type)
			{
				Control::color(hdc, type);
				::SetTextColor(hdc, ColoredText::getTextColor());
			}
		};
	
		class Button : public Control
		{
		public:
			Button(HWND hwndParent,
				LPCTSTR lpWindowName, DWORD dwStyle, int x, int y,
				int nWidth, int nHeight, int nIDDlgItem)
				: Control(hwndParent,
					TEXT("button"), lpWindowName, dwStyle, x, y,
					nWidth, nHeight, nIDDlgItem) {}
			Button(HWND hwndControl) : Control(hwndControl) {}
			int getCheck() const
			{
				return Button_GetCheck(hwndControl_);
			}
			void setCheck(int check)
			{
				Button_SetCheck(hwndControl_, check);
			}
		};
	
		class PushButton : public Button
		{
		public:
			PushButton(HWND hwndParent,
				LPCTSTR lpWindowName, int x, int y,
				int nWidth, int nHeight, int nIDDlgItem, DWORD dwStyle = 0)
				: Button(hwndParent,
					lpWindowName, BS_PUSHBUTTON | dwStyle, x, y,
					nWidth, nHeight, nIDDlgItem) {}
			PushButton(HWND hwndControl) : Button(hwndControl) {}
		};

		class UpDownButton : public PushButton
		{
			static WNDPROC &oldProc_()
			{
				static WNDPROC oldProc = 0;
				return oldProc;
			}
			static LRESULT CALLBACK upDownButtonProc(HWND hwndCtl, UINT iMsg, WPARAM wParam, LPARAM lParam)
			{
				switch (iMsg)
				{
				case WM_LBUTTONDOWN:
					FORWARD_WM_COMMAND(::GetParent(hwndCtl),
						::GetWindowLong(hwndCtl, GWL_ID), hwndCtl, BN_PUSHED, SendMessage);
					break;
				case WM_LBUTTONUP:
					FORWARD_WM_COMMAND(::GetParent(hwndCtl),
						::GetWindowLong(hwndCtl, GWL_ID), hwndCtl, BN_UNPUSHED, SendMessage);
					break;
				}
				return CallWindowProc(oldProc_(), hwndCtl, iMsg, wParam, lParam);
			}
		public:
			UpDownButton(HWND hwndParent,
				LPCTSTR lpWindowName, int x, int y,
				int nWidth, int nHeight, int nIDDlgItem, DWORD dwStyle = 0)
				: PushButton(hwndParent,
				lpWindowName, x, y, nWidth, nHeight, nIDDlgItem, dwStyle)
			{
				if (oldProc_() == 0)
					oldProc_() = reinterpret_cast<WNDPROC>(GetWindowLong(hwndControl_, GWL_WNDPROC));
				SetWindowLong(hwndControl_,
					GWL_WNDPROC, reinterpret_cast<LONG>(upDownButtonProc));
			}
			UpDownButton(HWND hwndControl) : PushButton(hwndControl) {}
		};
	
		class GroupBox : public Button  
		{
		public:
			GroupBox(HWND hwndParent,
				LPCTSTR lpWindowName, int x, int y,
				int nWidth, int nHeight, DWORD dwStyle = 0)
				: Button(hwndParent,
					lpWindowName, BS_GROUPBOX | dwStyle, x, y,
					nWidth, nHeight, -1) {}
			GroupBox(HWND hwndControl) : Button(hwndControl) {}
		};
	
		class Trackbar : public Control  
		{
		public:
			Trackbar(HWND hwndParent,
				int x, int y, int nWidth, int nHeight,
				int nIDDlgItem, DWORD dwStyle)
				: Control(hwndParent,
					TRACKBAR_CLASS, TEXT(""), dwStyle, x, y,
					nWidth, nHeight, nIDDlgItem) {}
			Trackbar(HWND hwndControl) : Control(hwndControl) {}
			void setPos(int logicalPos, bool redraw = true)
			{
				SendMessage(hwndControl_, TBM_SETPOS,
					(WPARAM)(BOOL)redraw ? TRUE : FALSE,
					(LPARAM)(LONG)logicalPos);
			}
			int getPos() const
			{
				return (int)(DWORD)SendMessage(hwndControl_, TBM_GETPOS, 0L, 0L);
			}
			void setRange(int min, int max)
			{
				SendMessage(hwndControl_, TBM_SETRANGE,
					static_cast<WPARAM>(TRUE),
					static_cast<LPARAM>(MAKELONG(min, max)));
			}
			Control getBuddy(bool fLocation)
			{
				return Control(reinterpret_cast<HWND>(SendMessage(hwndControl_,
					TBM_GETBUDDY, fLocation ? TRUE : FALSE, 0)));
			}
			void setBuddy(const Control &control, bool fLocation)
			{
				SendMessage(hwndControl_, TBM_SETBUDDY,
					fLocation == true ? TRUE : FALSE,
					reinterpret_cast<LPARAM>(control.getHandle()));  
			}
		};
	
	} // namespace ui

} // namespace cnc

#endif
